package org.example.domain.model.service.user;

import cn.hutool.core.text.CharSequenceUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.constant.RedisCacheConstant;
import org.example.domain.model.entity.user.UserErrorCode;
import org.example.domain.model.entity.user.mapstruct.UserAccountMapstruct;
import org.example.domain.model.entity.user.req.UserRegisterReq;
import org.example.domain.model.entity.user.req.UserUpdateReq;
import org.example.domain.model.entity.user.resp.UserMessageResp;
import org.example.domain.repository.RegistryCacheRepository;
import org.example.domain.repository.UserAccountRepository;
import org.example.domain.system.model.LoginUser;
import org.example.domain.system.model.UserAccount;
import org.example.utils.SecurityUtil;
import org.example.web.exception.ServerException;
import org.example.web.result.ResultInfo;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Optional;

@Service
@RequiredArgsConstructor
@Slf4j
public class UserDomainService {

    private final UserAccountRepository userAccountRepository;
    private final RedissonClient redissonClient;

    private final RegistryCacheRepository registryCacheRepository;

    public UserMessageResp getUserByUserName(String userName) {
        if (CharSequenceUtil.isBlank(userName)) {
            throw new ServerException(UserErrorCode.USER_NAME_SHOULD_NOT_EMPTY);
        }
        Optional<UserAccount> optional = userAccountRepository.getUserAccountByUserName(userName);
        UserAccount userAccount = optional.orElseThrow(() -> {
            throw new ServerException(UserErrorCode.USER_NULL);
        });
        return UserAccountMapstruct.INSTANCE.userAccountToUserMessageResp(userAccount);
    }

    @Transactional(rollbackFor = Exception.class)
    public ResultInfo registerUser(UserRegisterReq userRegisterReq) {
        if (this.hasRedisRegistryCache(userRegisterReq.getUserName())) {
            throw new ServerException(UserErrorCode.USER_NAME_EXIST);
        }
        RLock lock = redissonClient.getLock(RedisCacheConstant.LOCK_USER_REGISTER_KEY + userRegisterReq.getUserName());
        if (!lock.tryLock()) {
            throw new ServerException(UserErrorCode.USER_NAME_EXIST);
        }
        try {
            UserAccount userAccount = UserAccountMapstruct.INSTANCE.userRegisterReqToUserAccount(userRegisterReq);
            userAccount.encryptPassword(userAccount.getPassword());
            int inserted = userAccountRepository.registerUser(userAccount);
            if (inserted < 1) {
                throw new ServerException(UserErrorCode.USER_NAME_EXIST);
            }
            registryCacheRepository.addRedisRegistryCache(userAccount.getUserName());
        } catch (Exception exception) {
            log.error("Failed to register user=>{}", exception.getMessage());
            throw new ServerException(UserErrorCode.USER_NAME_EXIST);
        } finally {
            lock.unlock();
        }
        return ResultInfo.success("用户注册成功！");
    }

    public Boolean hasRedisRegistryCache(String userName) {
        return registryCacheRepository.hasRedisRegistryCache(userName);
    }


    public ResultInfo updateUser(UserUpdateReq userUpdateReq) {
        if (!CharSequenceUtil.equals(userUpdateReq.getUserName(), SecurityUtil.getUserName())) {
            return ResultInfo.error(UserErrorCode.USER_LOGIN_STATUS_IS_ABNORMAL);
        }
        UserAccount userAccount = UserAccountMapstruct.INSTANCE.userUpdateReqToUserAccount(userUpdateReq);
        this.userAccountRepository.updateUser(userAccount);
        return ResultInfo.success("用户修改成功！");
    }

    public UserAccount getUserAccountByUserName(String userName) {
        if (CharSequenceUtil.isBlank(userName)) {
            throw new ServerException(UserErrorCode.USER_NAME_SHOULD_NOT_EMPTY);
        }
        Optional<UserAccount> optional = userAccountRepository.getUserAccountByUserName(userName);
        return optional.orElseThrow(() -> {
            throw new ServerException(UserErrorCode.USER_NULL);
        });
    }

    public UserAccount getUserAccountByToken() {
        LoginUser loginUser = SecurityUtil.getLoginUser();
        UserAccount userAccount = new UserAccount();
        userAccount.setUserName(loginUser.getUserName());
        userAccount.setUserId(loginUser.getUserId());
        userAccount.setPhoneNumber(loginUser.getPhoneNumber());
        return userAccount;
    }
}
